﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Objects;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;

namespace DataGenerator.DefaultSets
{
    /// <summary>
    /// Type base.
    /// </summary>
    /// <typeparam name="T">Type of group.</typeparam>
    public abstract class SetBase<T> where T : class 
    {
        /// <summary>
        /// Property values
        /// </summary>
        private readonly Dictionary<string, object> _values = new Dictionary<string, object>();

        /// <summary>
        /// Initializes a new instance of the <see cref="SetBase&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="objectSet">The object set.</param>
        protected SetBase(ObjectSet<T> objectSet)
        {
            ObjectSet = objectSet;
        }

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Gets the object context.
        /// </summary>
        public ObjectContext ObjectContext { get { return ObjectSet.Context; } }

        /// <summary>
        /// Gets the context.
        /// </summary>
        public ObjectSet<T> ObjectSet { get; private set; }

        /// <summary>
        /// Gets the specified expression.
        /// </summary>
        /// <typeparam name="T">Property type</typeparam>
        /// <param name="expression">The expression.</param>
        /// <returns>Property value</returns>
        public T Get(Expression<Func<T>> expression)
        {
            var propertyName = PropertyNameProvider.GetPropertyName(expression);

            object value;
            if (_values.TryGetValue(propertyName, out value))
            {
                return (T)value;
            }

            value = GetDefaultValue(propertyName);
            _values.Add(propertyName, value);
            return (T)value;
        }

        /// <summary>
        /// Sets the specified expression.
        /// </summary>
        /// <typeparam name="T">Property type</typeparam>
        /// <param name="expression">The expression.</param>
        /// <param name="value">The value.</param>
        public void Set(Expression<Func<T>> expression, T value)
        {
            var propertyName = PropertyNameProvider.GetPropertyName(expression);

            if (_values.ContainsKey(propertyName))
            {
                _values[propertyName] = value;
            }
            else
            {
                _values.Add(propertyName, value);
            }

            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Gets the default value.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns>Type entity</returns>
        public T GetDefaultValue(string propertyName)
        {
            var entity = SearchDefaultValue(propertyName);
            if (entity != null)
            {
                return entity;
            }

            entity = ObjectSet.CreateObject();
            var property = entity.GetType().GetProperty("Name");
            if (property != null)
            {
                property.SetValue(entity, propertyName, BindingFlags.SetProperty, null, null,
                                  CultureInfo.InvariantCulture);
            }

            ObjectSet.CreateObject();
            ObjectSet.AddObject(entity);
            // ObjectContext.SaveChanges();
            ObjectContext.Detach(entity);
            return entity;
        }

        /// <summary>
        /// Searches the default value.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns>
        /// Default value.
        /// </returns>
        protected virtual T SearchDefaultValue(string name)
        {
            return null;
        }
    }
}
